
using System.Collections.Generic;
using UnityEngine;



using System.Collections;
using UObject = UnityEngine.Object;

using UnityEngine.Networking;
using System;
using UnityEngine.Events;

using UnityEngine.Assertions;
using System.Threading.Tasks;
using System.IO;
using UnityEngine.UI;

#if UNITY_EDITOR
using UnityEditor;
#endif

public class AssetBundleInfo
{
    public AssetBundle m_AssetBundle;
    public int m_ReferencedCount;

    public AssetBundleInfo(AssetBundle assetBundle)
    {
        m_AssetBundle = assetBundle;
        //m_ReferencedCount = 1;
    }
}

public class ResourceManager
{

    public static ResourceManager Instance = new ResourceManager();
    private string[] m_Variants = { };
    private AssetBundleManifest manifest;
    private AssetBundle shared;
    Dictionary<string, string[]> m_Dependencies = new Dictionary<string, string[]>();
    private Dictionary<string, AssetBundleInfo> bundles;
    private string assetPath = GameConfig.ResourceDir;
    private string m_BaseDownloadingURL = null;
    public static bool IsLoadByBundle = true;


    public static Dictionary<string, Texture2D> DownloadRawImage = new Dictionary<string, Texture2D>();
    public void Initialize(bool isLoadByBundle)
    {
        IsLoadByBundle = isLoadByBundle;
        if (IsLoadByBundle)
        {
            bundles = new Dictionary<string, AssetBundleInfo>();
            if (GameConfig.IsUpdate)
            {
                //m_BaseDownloadingURL = "http://172.16.0.100:5210/StreamingAssets/";
                m_BaseDownloadingURL = GameConfig.DataPath;
            }
            else
            {
                m_BaseDownloadingURL = Application.streamingAssetsPath + "/";
            }
        }
    }

    /// <summary>
    /// 初始化
    /// </summary>

    public async Task InitializeAsync(bool isLoadByBundle)
    {
        Initialize(isLoadByBundle);
        if (IsLoadByBundle)
        {
            AssetBundle assetbundle = LoadAssetBundle("StreamingAssets").m_AssetBundle;
            manifest = assetbundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
        }
    }

    public AssetBundle LoadBundle(string uri)
    {

        AssetBundle assetbundle = null;
        if (IsLoadByBundle)
        {
            byte[] stream = File.ReadAllBytes(uri);
            assetbundle = AssetBundle.LoadFromMemory(stream);
        }
        else
        {
            assetbundle = AssetBundle.LoadFromFile(uri);
        }
        return assetbundle;
    }

    public void DownloadActiveRawImage(RawImage rawImage,string imageUrl)
    {
        if (DownloadRawImage.ContainsKey(GameConfig.DownloadActiveUrl+imageUrl))
        {
            Debug.LogError("字典有");
            rawImage.texture = DownloadRawImage[GameConfig.DownloadActiveUrl+imageUrl];
        }
        else
        {
            Debug.LogError("字典没有");
            Utils.StartCoroutine(DownloadAndSetImage(rawImage, GameConfig.DownloadActiveUrl+imageUrl));
        }
    }
    IEnumerator DownloadAndSetImage(RawImage rawImage,string url)
    {
        // 使用WWW类来下载图片
        WWW www = new WWW(url);

        // 等待下载完成
        yield return www;

        // 检查是否有错误发生
        if (string.IsNullOrEmpty(www.error))
        {
            // 下载成功，设置RawImage的纹理
            rawImage.texture = www.texture;

            // 调整RawImage的尺寸以适应图片
            rawImage.SetNativeSize();
            DownloadRawImage.Add(url, www.texture);
            Debug.Log("Image downloaded and set successfully.");
        }
        else
        {
            // 下载失败，打印错误信息
            Debug.Log("Failed to download image: " + www.error);
        }
    }

    public async Task<AssetBundle> LoadBundleAsync(string uri)
    {
        //Debug.LogWarning("url:" + uri);
        Debug.LogWarning(uri);
        if (Application.platform==RuntimePlatform.Android)
        {
            uri = "jar:file://" + uri;
        }
        UnityWebRequest request = UnityWebRequestAssetBundle.GetAssetBundle(uri);
        await request.SendWebRequest();
        if (request.error!=null)
        {
            //throw new Exception(request.error+" url:"+uri);
            Debug.LogError(request.error + " url:" + uri);
            return null;
        }
        return DownloadHandlerAssetBundle.GetContent(request);
    }
    public GameObject LoadPrefab(string abname, string assetname)
    {
        return LoadAsset<GameObject>(abname, assetname, ".prefab");
    }

    public async Task<GameObject> LoadPrefabAsync(string abname, string assetname)
    {
        return await LoadAssetAsync<GameObject>(abname, assetname, ".prefab");
    }
    public async Task<Shader> LoadShaderAsync(string abname, string assetname)
    {
        return await LoadAssetAsync<Shader>(abname, assetname, ".shader");
    }
    public async Task<Shader> LoadUnityShaderAsync(string shaderName)
    {
        await Task.Yield();
        return Shader.Find(shaderName);
    }
    public async Task<Material> LoadMaterialAsync(string abname, string assetname)
    {
        return await LoadAssetAsync<Material>(abname, assetname, ".mat");
    }
    public Sprite LoadSprite(string abname, string assetname)
    {
        return LoadAsset<Sprite>(abname, assetname, ".png");
    }

    public Texture LoadTexture(string abname, string assetname)
    {
        return LoadAsset<Texture>(abname, assetname, ".png");
    }

    public Material LoadMaterial(string abname, string assetname)
    {
        return LoadAsset<Material>(abname, assetname, ".mat");
    }

    public async Task<Sprite> LoadSpriteAsync(string abname, string assetname)
    {
        return await LoadAssetAsync<Sprite>(abname, assetname, ".png");
    }

    public AudioClip LoadAudio(string abname, string assetname)
    {
        AudioClip clip = LoadAsset<AudioClip>(abname, assetname, ".mp3");
        if (clip == null)
        {
            clip = LoadAsset<AudioClip>(abname, assetname, ".wav");
            if (clip == null)
            {
                clip = LoadAsset<AudioClip>(abname, assetname, ".ogg");
            }
        }

        return clip;
    }

    public async Task<AudioClip> LoadAudioAsync(string abname, string assetname)
    {
        AudioClip clip = await LoadAssetAsync<AudioClip>(abname, assetname, ".mp3");
        if (clip == null)
        {
            clip = await LoadAssetAsync<AudioClip>(abname, assetname, ".wav");
            if (clip == null)
            {
                clip = await LoadAssetAsync<AudioClip>(abname, assetname, ".ogg");
            }
        }
        return clip;
    }
    public async Task<Texture> LoadTextureAsync(string abname, string assetname)
    {
        return await LoadAssetAsync<Texture>(abname, assetname, ".png");
    }
    public async Task<Sprite[]> LoadUISpritesAsync(string abname, string assetname)
    {
        return await LoadAllAssetsAsync<Sprite>(abname, assetname);
    }
    public TextAsset LoadTextAsset(string abname, string assetname)
    {
        return LoadAsset<TextAsset>(abname, assetname, ".bytes");
    }
    public async Task<TextAsset> LoadTextAssetAsync(string abname, string assetname)
    {

        return await LoadAssetAsync<TextAsset>(abname, assetname, ".bytes");
    }

    public TextAsset LoadJsonAsset(string abname, string assetname)
    {

        return LoadAsset<TextAsset>(abname, assetname, ".json");
    }
    public async Task<TextAsset> LoadJsonAssetAsync(string abname, string assetname)
    {

        return await LoadAssetAsync<TextAsset>(abname, assetname, ".json");
    }


    /// <summary>
    /// 载入素材
    /// </summary>
    public async Task<T> LoadAssetAsync<T>(string abname, string assetname, string suffix = null) where T : UnityEngine.Object
    {
        T t = null;
#if UNITY_EDITOR
        if (!EditorApplication.isPlaying || !IsLoadByBundle)
#else
        if (!IsLoadByBundle)
#endif
        {
            string name = abname + "/" + assetname;
            //string name = abname;

#if UNITY_EDITOR
            name = "Assets/" + GameConfig.ResourceDir + name;
            if (suffix != null)
            {
                name = name + suffix;
            }
            //Debug.LogError(name);
            t = AssetDatabase.LoadAssetAtPath<T>(name);
            await Task.Yield();
#else
            t = Resources.Load<T>(name);
#endif

            if (t == null)
            {
                //Debug.LogError("资源不存在,名字为" + name);
            }
        }
        else
        {
            try
            {
                AssetBundleInfo bundle = await LoadAssetBundleAsync(abname);
                t = bundle.m_AssetBundle.LoadAsset<T>(assetname);
                if (t == null)
                {
                    Debug.LogError("资源不存在,名字为" + abname + " assetname:" + assetname);
                }
            }
            catch(Exception e)
            {
                Debug.LogError(e.Message);
            }
            
        }
        return t;
    }

    public T LoadAsset<T>(string abname, string assetname, string suffix = null) where T : UnityEngine.Object
    {
        T t = null;
#if UNITY_EDITOR
        if (!EditorApplication.isPlaying || !IsLoadByBundle)
#else
        if (!IsLoadByBundle)
#endif
        {
            string name = abname + "/" + assetname;

#if UNITY_EDITOR
            name = "Assets/" + GameConfig.ResourceDir + name;
            if (suffix != null)
            {
                name = name + suffix;
            }
            //Debug.LogError(name);
            
            t = AssetDatabase.LoadAssetAtPath<T>(name);
#else
            t = Resources.Load<T>(name);
            name = name.ToLower();
#endif
            if (t == null)
            {
                // Debug.LogError("资源不存在,名字为" + name);
            }
        }
        else
        {
            //Debug.LogError("12312312312312312312312===="+assetname);
            try
            {
                AssetBundleInfo bundle = LoadAssetBundle(abname);
                t = bundle.m_AssetBundle.LoadAsset<T>(assetname);
            }
            catch (Exception e)
            {

                Debug.LogError("资源不存在,名字为" + abname + " assetname:" + assetname+ "====="+e.Message);
            }
           
            //if (t == null)
            //{
               
            //}
        }
        return t;
    }

    public string GetAbName(string abname)
    {
        if (!abname.EndsWith(GameConfig.ExtName))
        {
            abname = (GameConfig.ResourceDir + abname + GameConfig.ExtName).ToLower();
        }
        return abname;
    }
    public T[] LoadAllAssets<T>(string abname) where T : UnityEngine.Object
    {
        string path = (GameConfig.ResourceDir + abname).ToLower();
        AssetBundleInfo bundle = LoadAssetBundle(path);
        return bundle.m_AssetBundle.LoadAllAssets<T>();
    }
    public async Task<T[]> LoadAllAssetsAsync<T>(string abname, string assetname) where T : UnityEngine.Object
    {
        string path = (GameConfig.ResourceDir + abname+ assetname).ToLower();
        AssetBundleInfo bundle = await LoadAssetBundleAsync(path);
        return bundle.m_AssetBundle.LoadAllAssets<T>();
    }
    public void LoadPrefab(string abName, string[] assetNames)
    {
        abName = abName.ToLower();
        List<UObject> result = new List<UObject>();
        for (int i = 0; i < assetNames.Length; i++)
        {
            UObject go = LoadAsset<UObject>(abName, assetNames[i]);
            if (go != null) result.Add(go);
        }
    }
    public async void LoadPrefabAsync(string abName, string[] assetNames)
    {
        abName = abName.ToLower();
        List<UObject> result = new List<UObject>();
        for (int i = 0; i < assetNames.Length; i++)
        {
            UObject go = await LoadAssetAsync<UObject>(abName, assetNames[i]);
            if (go != null) result.Add(go);
        }
    }
    /// <summary>
    /// 载入AssetBundle
    /// </summary>
    /// <param name="abname"></param>
    /// <returns></returns>
    public AssetBundleInfo LoadAssetBundle(string abname)
    {
        Debug.LogError("LoadAssetBundle==========");
        if (abname!= "StreamingAssets")
        {
            Debug.LogError("LoadAssetBundle==========11111");
            abname = GetAbName(abname);
        }
        Debug.LogError("LoadAssetBundle==========2222");
        AssetBundleInfo bundle = null;
        if (!bundles.ContainsKey(abname))
        {
            //byte[] stream = null;
            string uri = m_BaseDownloadingURL + abname;
            Debug.LogWarning("LoadFile::>> " + uri);
            LoadDependencies(abname);

            //stream = File.ReadAllBytes(uri);
            AssetBundle m_bundle = LoadBundle(uri); //关联数据的素材绑定
            bundle = new AssetBundleInfo(m_bundle);
            bundles.Add(abname, bundle);
        }
        else
        {
            bundles.TryGetValue(abname, out bundle);

        }
        bundles[abname].m_ReferencedCount++;
        return bundle;
    }
    /// <summary>
    /// 载入AssetBundle
    /// </summary>
    /// <param name="abname"></param>
    /// <returns></returns>
    public async Task<AssetBundleInfo> LoadAssetBundleAsync(string abname)
    {
        abname = GetAbName(abname);
        AssetBundleInfo bundle = null;
        if (!bundles.ContainsKey(abname))
        {
            //byte[] stream = null;
            string uri = m_BaseDownloadingURL + abname;
            Debug.LogWarning("LoadFile::>> " + uri);
            await LoadDependenciesAsync(abname);

            //stream = File.ReadAllBytes(uri);
            AssetBundle m_bundle = await LoadBundleAsync(uri); //关联数据的素材绑定
            bundle = new AssetBundleInfo(m_bundle);
            bundles.Add(abname, bundle);
        }
        else
        {
            await Task.Yield();
            bundles.TryGetValue(abname, out bundle);
         
        }
        bundles[abname].m_ReferencedCount++;
        return bundle;
    }
    /// <summary>
    /// 载入依赖
    /// </summary>
    /// <param name="name"></param>
    void LoadDependencies(string name)
    {
        if (manifest == null)
        {
            Debug.LogError("Please initialize AssetBundleManifest by calling AssetBundleManager.Initialize()");
            return;
        }
        // Get dependecies from the AssetBundleManifest object..
        string[] dependencies = manifest.GetAllDependencies(name);
        if (dependencies.Length == 0) return;

        m_Dependencies.Add(name, dependencies);
        for (int i = 0; i < dependencies.Length; i++)
            dependencies[i] = RemapVariantName(dependencies[i]);

        // Record and load all dependencies.
        for (int i = 0; i < dependencies.Length; i++)
        {
            LoadAssetBundle(dependencies[i]);
        }
    }
    /// <summary>
    /// 载入依赖
    /// </summary>
    /// <param name="name"></param>
    async Task LoadDependenciesAsync(string name)
    {
        if (manifest == null)
        {
            Debug.LogError("Please initialize AssetBundleManifest by calling AssetBundleManager.Initialize()");
            return;
        }
        // Get dependecies from the AssetBundleManifest object..
        string[] dependencies = manifest.GetAllDependencies(name);
        if (dependencies.Length == 0) return;

        m_Dependencies.Add(name, dependencies);
        for (int i = 0; i < dependencies.Length; i++)
            dependencies[i] = RemapVariantName(dependencies[i]);

        // Record and load all dependencies.
        for (int i = 0; i < dependencies.Length; i++)
        {
            await LoadAssetBundleAsync(dependencies[i]);
        }
    }

    // Remaps the asset bundle name to the best fitting asset bundle variant.
    string RemapVariantName(string assetBundleName)
    {
        string[] bundlesWithVariant = manifest.GetAllAssetBundlesWithVariant();

        // If the asset bundle doesn't have variant, simply return.
        if (System.Array.IndexOf(bundlesWithVariant, assetBundleName) < 0)
            return assetBundleName;

        string[] split = assetBundleName.Split('.');

        int bestFit = int.MaxValue;
        int bestFitIndex = -1;
        // Loop all the assetBundles with variant to find the best fit variant assetBundle.
        for (int i = 0; i < bundlesWithVariant.Length; i++)
        {
            string[] curSplit = bundlesWithVariant[i].Split('.');
            if (curSplit[0] != split[0])
                continue;

            int found = System.Array.IndexOf(m_Variants, curSplit[1]);
            if (found != -1 && found < bestFit)
            {
                bestFit = found;
                bestFitIndex = i;
            }
        }
        if (bestFitIndex != -1)
            return bundlesWithVariant[bestFitIndex];
        else
            return assetBundleName;
    }

    /// <summary>
    /// 销毁资源
    /// </summary>
    void OnDestroy()
    {
        if (shared != null) shared.Unload(true);
        if (manifest != null) manifest = null;
        Debug.Log("~ResourceManager was destroy!");
    }

    /// <summary>
    /// 此函数交给外部卸载专用，自己调整是否需要彻底清除AB
    /// </summary>
    /// <param name="abName"></param>
    /// <param name="isThorough"></param>
    /// <param name="dirct">直接卸载</param>
    public void UnloadAssetBundle(string name, bool isThorough)
    {
        if (IsLoadByBundle)
        {
            string abName = GetAbName(name);
            UnloadAssetBundle0(abName, isThorough);
            UnloadDependencies(abName, isThorough);

        }
        Resources.UnloadUnusedAssets();
    }

    void UnloadAssetBundle0(string abName, bool isThorough)
    {
        AssetBundleInfo bundle = null;
        bundles.TryGetValue(abName, out bundle);
        if (bundle != null)
        {
            bundle.m_ReferencedCount--;
            if (bundle != null && bundle.m_ReferencedCount == 0)
            {
                Debug.Log("卸载资源:" + abName);
                bundle.m_AssetBundle.Unload(isThorough);
                bundles.Remove(abName);
            }
        }
    }


    void UnloadDependencies(string abName, bool isThorough)
    {
        string[] dependencies = null;
        if (!m_Dependencies.TryGetValue(abName, out dependencies))
            return;

        // Loop dependencies.
        foreach (var dependency in dependencies)
        {
            UnloadAssetBundle0(dependency, isThorough);
        }
        m_Dependencies.Remove(abName);
    }





    //public void AsynLoadBundle(string abName, Action callBack)
    //{
    //    GameManager.Instance.StartCoroutine(AsynLoadAssetBundle(abName, callBack));
    //}

    //async Task AsynLoadAssetBundle(string abName, Action callBack)
    //{
    //    if (!bundles.ContainsKey(abName))
    //    {
    //        string url = m_BaseDownloadingURL + abName;

    //        await AsynLoadDependencies(abName);
    //        UnityWebRequest webRequest = UnityWebRequest.Get(url);
    //        await webRequest.SendWebRequest();
    //        if (webRequest.error != null)
    //        {
    //            Debug.LogError("url:" + url);
    //            Debug.LogError(webRequest.error);
    //        }
    //        if (webRequest.isDone)
    //        {
    //            AssetBundleCreateRequest request = AssetBundle.LoadFromMemoryAsync(webRequest.downloadHandler.data);
    //            await request.;

    //            bundles.Add(abName, new AssetBundleInfo(request.assetBundle));
    //            AssetBundleRequest bundleRequest = request.assetBundle.LoadAllAssetsAsync();
    //            yield return bundleRequest;
    //            //Debug.LogError("bundleRequest.progress:" + bundleRequest.progress);
    //        }
    //    }
    //    else
    //    {
    //        bundles[abName].m_ReferencedCount++;
    //    }
    //    callBack?.Invoke();
    //}

    //Task AsynLoadDependencies(string name)
    //{

    //    string[] dependencies = manifest.GetAllDependencies(name);
    //    if (dependencies.Length == 0)
    //    {
    //        yield break;
    //    }
    //    for (int i = 0; i < dependencies.Length; i++)
    //        dependencies[i] = RemapVariantName(dependencies[i]);

    //    // Record and load all dependencies.
    //    for (int i = 0; i < dependencies.Length; i++)
    //    {
    //        yield return GameManager.Instance.StartCoroutine(AsynLoadAssetBundle(dependencies[i], null));
    //    }
    //}
}
